package org.example.cell;

import org.example.annotation.Logic;
import org.example.annotation.TableName;
import org.example.domain.JDBCInfo;
import org.example.domain.Page;
import org.example.utils.DataSourceUtils;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.*;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * Cell 本体定义了适用于泛型的查询，插入，删除，更新操作
 * XXXCell 继承Cell，并指定要使用哪个具体的类型即可直接无缝使用方法
 * @param <T>
 */
public class Cell<T> implements BaseCell<T> {

    /**
     * 获取泛型的具体类型的方法
     * @return 泛型的具体类型
     */
    public Class getCellType() {
        //获取到当前类的父类的符号类型
        Type superClassType = this.getClass().getGenericSuperclass();
        //如果父类是泛型
        if(superClassType instanceof ParameterizedType) {
            //类型强转，把Class转换成ParameterizedType
            ParameterizedType parameterizedType = (ParameterizedType) superClassType;
            //获取到泛型类型的值
            Type[] actualType = parameterizedType.getActualTypeArguments();
            if(actualType.length>0) {
                return (Class) actualType[0];
            }else {
                return null;
            }
        }else {
            return null;
        }
    }

    @Override
    public List<T> selectList(Page page, JDBCInfo jdbcInfo) throws SQLException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IntrospectionException {
        List<T> result = new ArrayList<>();
        Class targetClass = getCellType();
        DataSourceUtils dataSourceUtils = new DataSourceUtils();
        dataSourceUtils.initSimpleConnection(jdbcInfo.getUrl(), jdbcInfo.getUser(), jdbcInfo.getPassword());
        String sql = "select * from "+getAnnotationTableName(targetClass)+" where "+getAnnotationLogicDeleted(targetClass)+" = 0 limit ?, ?";
        int index = (page.getNo()-1)*page.getSize();
        ResultSet resultSet = dataSourceUtils.doSQL(sql, index, page.getSize());
        ResultSetMetaData metaData = resultSet.getMetaData();
        int columnCount = metaData.getColumnCount();
        while(resultSet.next()) {
            T item = (T) targetClass.getConstructor(null).newInstance();
            for(int i = 1; i <= columnCount; i++) {
                String columnLabel = metaData.getColumnLabel(i);
                Object columnValue = resultSet.getObject(columnLabel);

                PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnLabel, targetClass);
                Method method = propertyDescriptor.getWriteMethod();
                method.invoke(item, columnValue);
            }
            result.add(item);
        }

        return result;
    }

    /**
     * INSERT
     * INTO `lifeDB`.`movies_metadata`
     * (`budget`, `genres`, `homepage`, `id`, `imdb_id`, `original_language`, `original_title`, `popularity`, `poster_path`, `release_date`, `revenue`, `runtime`, `status`, `title`, `vote_average`, `vote_count`, `deleted`)
     * VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
     * @param t          要插入的对象
     * @param jdbcInfo   JDBC连接信息
     * @return
     * @throws SQLException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     * @throws IntrospectionException
     */
    @Override
    public boolean insert(T t, JDBCInfo jdbcInfo) throws SQLException, InvocationTargetException, IllegalAccessException, IntrospectionException {
        Class targetClass = getCellType();
        String tableName = getAnnotationTableName(targetClass);
        String dSQL = "INSERT INTO "+tableName+" (";
        String tail = "VALUES (";
        DataSourceUtils dataSourceUtils = new DataSourceUtils();
        dataSourceUtils.initSimpleConnection(jdbcInfo.getUrl(), jdbcInfo.getUser(), jdbcInfo.getPassword());
        String sql = "select * from "+tableName+" where deleted = 0 limit 1";
        ResultSet resultSet = dataSourceUtils.doSQL(sql);
        ResultSetMetaData metaData = resultSet.getMetaData();
        int columnCount = metaData.getColumnCount();

        Object[] objects = new Object[columnCount];
        int index = 0;
        for(int i = 0; i < columnCount; i++) {
            String colName = metaData.getColumnLabel(i+1);
            PropertyDescriptor propertyDescriptor = new PropertyDescriptor(colName, targetClass);
            Method method = propertyDescriptor.getReadMethod();
            Object obj = method.invoke(t);
            objects[index++] = obj;
            if(i == columnCount-1) {
                dSQL = dSQL + colName;
                tail = tail + "?";
            }else {
                dSQL = dSQL + colName + ", ";
                tail = tail + "?, ";
            }
        }
        dSQL = dSQL + ") ";
        tail = tail + ")";
        dSQL = dSQL + tail;
        System.out.println(dSQL);
        dataSourceUtils.doSQL(dSQL, objects);
        return true;
    }

    /**
     * UPDATE
     * `lifeDB`.`movies_metadata`
     * SET `budget` = ?, `genres` = ?, `homepage` = ?, `id` = ?, `imdb_id` = ?, `original_language` = ?, `original_title` = ?, `popularity` = ?, `poster_path` = ?, `release_date` = ?, `revenue` = ?, `runtime` = ?, `status` = ?, `title` = ?, `vote_average` = ?, `vote_count` = ?, `deleted` = ? WHERE id = ?;
     * @param t         要更新的数据
     * @param jdbcInfo  数据库连接信息
     * @return
     * @throws SQLException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     * @throws IntrospectionException
     */
    @Override
    public boolean update(T t, JDBCInfo jdbcInfo) throws SQLException, InvocationTargetException, IllegalAccessException, IntrospectionException {
        Class targetClass = getCellType();
        String tableName = getAnnotationTableName(targetClass);
        String dSQL = "update "+tableName+" set ";
        DataSourceUtils dataSourceUtils = new DataSourceUtils();
        dataSourceUtils.initSimpleConnection(jdbcInfo.getUrl(), jdbcInfo.getUser(), jdbcInfo.getPassword());
        String sql = "select * from "+tableName+" where deleted = 0 limit 1";
        ResultSet resultSet = dataSourceUtils.doSQL(sql);
        ResultSetMetaData metaData = resultSet.getMetaData();
        int columnCount = metaData.getColumnCount();
        Object[] objects = new Object[columnCount];
        int index = 0;
        for(int i = 0; i < columnCount; i++) {
            String colName = metaData.getColumnLabel(i+1);
            //将ID排除在外，ID要作为where条件，不和修改内容在一起
            if(!"id".equals(colName)) {
                PropertyDescriptor propertyDescriptor = new PropertyDescriptor(colName, targetClass);
                Method method = propertyDescriptor.getReadMethod();
                Object obj = method.invoke(t);
                objects[index++] = obj;
                if(index == columnCount-1) {
                    dSQL = dSQL + colName + "= ? ";
                }else {
                    dSQL = dSQL + colName + "= ?, ";
                }
            }
        }
        dSQL = dSQL + "where id = ?";
        PropertyDescriptor propertyDescriptor = new PropertyDescriptor("id", targetClass);
        Method method = propertyDescriptor.getReadMethod();
        Object obj = method.invoke(t);
        objects[index] = obj;
        dataSourceUtils.doSQL(dSQL, objects);
        return true;
    }

    /**
     *
     * @param id        要删除的数据的ID
     * @param jdbcInfo  数据库连接信息
     * @return
     */
    @Override
    public boolean deleteByID(Integer id, JDBCInfo jdbcInfo) {
        Class targetClass = getCellType();
        String sql = "update "+getAnnotationTableName(targetClass)+" set "+getAnnotationLogicDeleted(targetClass)+"=1 "+"where id = ?";
        DataSourceUtils dataSourceUtils = new DataSourceUtils();
        dataSourceUtils.initSimpleConnection(jdbcInfo.getUrl(), jdbcInfo.getUser(), jdbcInfo.getPassword());
        dataSourceUtils.doSQL(sql, id);
        return true;
    }

    /**
     * 检查类是否有TableName注解，有的话，返回其name
     * @param targetClass 要检查的类
     * @return
     */
    public String getAnnotationTableName(Class targetClass) {
        //获取到注解
        TableName table = (TableName) targetClass.getAnnotation(TableName.class);
        //获取注解的内容
        String tableName = table.name();
        return tableName;
    }

    /**
     * 检查Class对应实体类中，哪些字段有逻辑删除注解
     * @param targetClass  要检查的Class
     * @return
     */
    public String getAnnotationLogicDeleted(Class targetClass) {
        //获取到Class的所有字段(包括私有类型)
        Field[] fields = targetClass.getDeclaredFields();
        for(int i = 0; i < fields.length; i++) {
            //如果字段有逻辑删除注解，返回字段名
            if(fields[i].isAnnotationPresent(Logic.class)) {
                return fields[i].getName();
            }
        }
        return "deleted";
    }
}
